package com.company.planner.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.company.planner.entity.*;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.Transaction;
import com.haulmont.cuba.core.TransactionalDataManager;
import com.haulmont.cuba.core.global.DataManager;
import org.apache.http.HttpHost;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service(EsService.NAME)
public class EsServiceBean implements EsService {

    @Inject
    protected DataManager dataManager;
    @Inject
    private Logger log;
    @Inject
    private Persistence persistence;
    @Inject
    private TransactionalDataManager transactionalDataManager;

    @Override
    public RestHighLevelClient loadEsRestHighLevelClient(String ip, int port) {
        HttpHost httpHost = new HttpHost(ip, port, "http");
        return new RestHighLevelClient(RestClient.builder(httpHost));
    }

    @Override
    public boolean buildClusterState(String ip, int port) {
        log.info("EsServiceBean.getClusterState ip:{},port:{}", ip, port);
        RestHighLevelClient restHighLevelClient = loadEsRestHighLevelClient(ip, port);
        log.info("EsServiceBean.getClusterState restHighLevelClient:{}", restHighLevelClient);

        try {
            //设置请求
            Request request = new Request("GET", "/_cluster/state/master_node,metadata,nodes");
            Response response = restHighLevelClient.getLowLevelClient().performRequest(request);
            //获取响应对象
            String responseStr = EntityUtils.toString(response.getEntity());

            //解析
            JSONObject jsonObject = JSONObject.parseObject(responseStr);

            String clusterUuid = jsonObject.getString("cluster_uuid");
            String clusterName = jsonObject.getString("cluster_name");
            String masterNode = jsonObject.getString("master_node");
            log.info("clusterUuid:{} -- clusterName:{} -- masterNode:{}", clusterUuid, clusterName, masterNode);

//            if (existEsClusterConfig(clusterName)) {
//                return false;
//            }

            //取节点信息
            JSONObject nodes = jsonObject.getJSONObject("nodes");

            List<EsClusterConfig> esClusterConfigs = new ArrayList<>();
            Set<Map.Entry<String, Object>> entries = nodes.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                JSONObject node = (JSONObject) entry.getValue();
                String nodeName = node.getString("name");
                String ephemeralId = node.getString("ephemeral_id");
                String transportAddress = node.getString("transport_address");
                String[] address = transportAddress.split(":");
                log.info("nodeName:{} -- ephemeralId:{} -- transportAddress:{} -- address:{}", nodeName, ephemeralId, transportAddress, address);
                //加入到配置
                EsClusterConfig esClusterConfig = dataManager.create(EsClusterConfig.class);
                esClusterConfig.setClusterUuid(clusterUuid);
                esClusterConfig.setClusterName(clusterName);
                esClusterConfig.setNodeIp(address[0]);
                esClusterConfig.setNodeHttpAddress(address[0]);
                esClusterConfig.setNodeId(key);
                esClusterConfig.setNodeName(nodeName);
                esClusterConfig.setNodeEphemeralId(ephemeralId);
                esClusterConfig.setNodeTransportAddress(transportAddress);
                esClusterConfig.setActive(ActiveEnum.YES);
                if (masterNode.equals(key)) {
                    //加载主节点
                    esClusterConfig.setMasterNode(masterNode);
                    esClusterConfig.setNodeType(ClusterType.MASTER);
//                    esClusterConfigMaster.setNodePort(port);
                } else {
                    //加载从节点
                    esClusterConfig.setNodeType(ClusterType.SLAVE);
                }
                esClusterConfigs.add(esClusterConfig);
            }

            batchAddEsClusterConfig(esClusterConfigs);

            List<Indexs> indexs = new ArrayList<>();
            List<Mappings> mappingsList = new ArrayList<>();

            //取metadata.indices
            JSONObject metadata = jsonObject.getJSONObject("metadata");
            String cluster_uuid = metadata.getString("cluster_uuid");
            JSONObject clusterCoordination = metadata.getJSONObject("cluster_coordination");
            if (cluster_uuid.equals(clusterUuid)) {
                List<EsClusterConfig> esClusterConfigsNew = new ArrayList<>();
                //索引与node关系
                JSONArray lastAcceptedConfigs = clusterCoordination.getJSONArray("last_accepted_config");
                for (Object acceptedConfig : lastAcceptedConfigs) {
                    for (EsClusterConfig esClusterConfig : esClusterConfigs) {
                        if (esClusterConfig.getNodeId().equals(acceptedConfig)) {
                            esClusterConfigsNew.add(esClusterConfig);
                        }
                    }
                }
                //索引
                JSONObject indices = metadata.getJSONObject("indices");
                Set<Map.Entry<String, Object>> index = indices.entrySet();
                for (Map.Entry<String, Object> objectEntry : index) {
                    //取自定义的index索引项
                    if (!objectEntry.getKey().contains(".")) {
                        String key = objectEntry.getKey();
                        JSONObject entryValue = (JSONObject) objectEntry.getValue();
                        String state = entryValue.getString("state");
                        JSONObject settings = entryValue.getJSONObject("settings");
                        JSONObject settingsJsonObject = settings.getJSONObject("index");
                        String uuid = settingsJsonObject.getString("uuid");
                        JSONObject mappings = entryValue.getJSONObject("mappings");
                        Indexs ind = dataManager.create(Indexs.class);
                        ind.setIndexId(uuid);
                        ind.setIndexName(key);
                        ind.setMappings(mappings.toString());
                        ind.setState(state.equals(IndexState.OPEN.getId()) ? IndexState.OPEN : IndexState.CLOSE);
                        ind.setEsClusterConfigs(esClusterConfigsNew);
                        indexs.add(ind);
                        //去索引映射信息
                        JSONObject doc = mappings.getJSONObject("_doc");
                        JSONObject properties = doc.getJSONObject("properties");
                        Set<Map.Entry<String, Object>> prop = properties.entrySet();
                        Mappings mappingFuji = dataManager.create(Mappings.class);
                        if (prop.size() > 0) {
                            mappingFuji.setIndexs(ind);
                            mappingFuji.setLevel(NestedLevel.ZERO);
                            mappingsList.add(mappingFuji);
                        }
                        for (Map.Entry<String, Object> typeEntry : prop) {
                            String name = typeEntry.getKey();
                            JSONObject typeValue = (JSONObject) typeEntry.getValue();
                            String type = typeValue.getString("type");
                            Mappings mapping = dataManager.create(Mappings.class);
                            mapping.setMappingName(name);
                            mapping.setMappingType(type);
                            mapping.setIndexs(ind);
                            mapping.setLevel(NestedLevel.ONE);
                            mapping.setMapping(mappingFuji);
                            mappingsList.add(mapping);
                            //如果存在一级嵌套对象
                            if ((type == null || "".equals(type)) || type.equals("nested")) {
                                JSONObject nestedProp = typeValue.getJSONObject("properties");
                                Set<Map.Entry<String, Object>> typeNested = nestedProp.entrySet();
                                for (Map.Entry<String, Object> entry : typeNested) {
                                    String nestedName = entry.getKey();
                                    JSONObject nestedValue = (JSONObject) entry.getValue();
                                    String nestedType = nestedValue.getString("type");
                                    Mappings mapping2 = dataManager.create(Mappings.class);
                                    mapping2.setMappingName(nestedName);
                                    mapping2.setMappingType(nestedType);
                                    mapping2.setMapping(mapping);
                                    mapping2.setIndexs(ind);
                                    mapping2.setLevel(NestedLevel.TWO);
                                    mappingsList.add(mapping2);
                                    //如果存在二级嵌套对象
                                }
                            }
                        }
                    }
                }
                batchAddIndexs(indexs);
                batchAddMappings(mappingsList);

            }
        } catch (IOException e) {
            log.error("EsServiceBean.getClusterState Error", e);
        } finally {
            try {
                restHighLevelClient.close();
            } catch (IOException e) {
                log.error("EsServiceBean.getClusterState Error", e);
            }
        }
        return true;
    }

    private void batchAddMappings(List<Mappings> mappingsList) {
//        降序
//        List<Mappings> sortCollect = mappingsList.stream().sorted(Comparator.comparing(Mappings::getLevel).reversed()).collect(Collectors.toList());
        List<Mappings> sortCollect = mappingsList.stream().sorted(Comparator.comparing(Mappings::getLevel)).collect(Collectors.toList());
        try (Transaction tx = persistence.createTransaction()) {
            sortCollect.forEach(mappings -> transactionalDataManager.save(mappings));
            tx.commit();
        } catch (Exception e) {
            log.error("Error", e);
        }
    }

    private void batchAddIndexs(List<Indexs> indexs) {
        try (Transaction tx = persistence.createTransaction()) {
            indexs.forEach(indexs1 -> transactionalDataManager.save(indexs1));
            tx.commit();
        } catch (Exception e) {
            log.error("Error", e);
        }
    }

    @Override
    public void batchAddEsClusterConfig(List<EsClusterConfig> esClusterConfigs) {
        try (Transaction tx = persistence.createTransaction()) {
            //取list中master信息
            EsClusterConfig masterConfig = esClusterConfigs.stream().filter(esClusterConfig -> esClusterConfig.getNodeType().equals(ClusterType.MASTER)).collect(Collectors.toList()).get(0);
            transactionalDataManager.save(masterConfig);
            tx.commit();
            List<EsClusterConfig> slaveConfigs = esClusterConfigs.stream().filter(esClusterConfig -> esClusterConfig.getNodeType().equals(ClusterType.SLAVE)).collect(Collectors.toList());
            slaveConfigs.forEach(esClusterConfig -> {
                if (esClusterConfig.getNodeType().equals(ClusterType.SLAVE)) {
                    esClusterConfig.setParentClusterConfig(masterConfig);
                    transactionalDataManager.save(esClusterConfig);
                }
            });
            tx.commit();
        } catch (Exception e) {
            log.error("Error", e);
        }
    }

    @Override
    public boolean existEsClusterConfig(String clusterName) {
        List<EsClusterConfig> esClusterConfigs = dataManager.load(EsClusterConfig.class)
                .query("select e from planner_EsClusterConfig e where e.clusterName = :name")
                .parameter("name", clusterName)
                .list();
        log.info("existEsClusterConfig.esClusterConfigs.size:{}", esClusterConfigs.size());
        return !esClusterConfigs.isEmpty();
    }

    @Override
    public boolean existIkAnalysisRule(Indexs indexs) {
        List<IkAnalysisRule> esClusterConfigs = dataManager.load(IkAnalysisRule.class)
                .query("select e from planner_IkAnalysisRule e where e.indexs = :indexs and e.active = :active")
                .parameter("indexs", indexs)
                .parameter("active", ActiveEnum.YES)
                .list();
        log.info("existEsClusterConfig.esClusterConfigs.size:{}", esClusterConfigs.size());
        return !esClusterConfigs.isEmpty();
    }
}